package com.example.demo;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.*;

public class SM2Util {

    private static final String PRIVATE_HEX_KEY = "44b54ccbbe2bcbf6f1ad03f864a088a8fbf6dda1f9c847e54eac1ac621f2b39a";

    private static final String CURVE_NAME = "sm2p256v1";

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成SM2密钥对
     */
    public static KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(CURVE_NAME);
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
        keyPairGenerator.initialize(ecGenParameterSpec, new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 将普通字符串类型的公钥转换为 PublicKey 对象
     */
    public static PublicKey getPublicKeyFromString(String hexPublicKey) throws Exception {
        byte[] keyBytes = Hex.decode(hexPublicKey);
        X9ECParameters x9ECParameters = GMNamedCurves.getByName(CURVE_NAME);
        EllipticCurve ellipticCurve = new EllipticCurve(
                new ECFieldFp(x9ECParameters.getCurve().getField().getCharacteristic()),
                x9ECParameters.getCurve().getA().toBigInteger(),
                x9ECParameters.getCurve().getB().toBigInteger()
        );
        ECPoint point = new ECPoint(
                x9ECParameters.getCurve().decodePoint(keyBytes).getAffineXCoord().toBigInteger(),
                x9ECParameters.getCurve().decodePoint(keyBytes).getAffineYCoord().toBigInteger()
        );
        ECParameterSpec ecParameterSpec = new ECParameterSpec(
                ellipticCurve,
                new ECPoint(
                        x9ECParameters.getG().getAffineXCoord().toBigInteger(),
                        x9ECParameters.getG().getAffineYCoord().toBigInteger()
                ),
                x9ECParameters.getN(),
                x9ECParameters.getH().intValue()
        );
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecParameterSpec);
        return keyFactory.generatePublic(pubSpec);
    }

    /**
     * 将普通字符串类型的私钥转换为 PrivateKey 对象
     */
    public static PrivateKey getPrivateKeyFromString(String hexPrivateKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        byte[] keyBytes = Hex.decode(hexPrivateKey);
        X9ECParameters x9ECParameters = GMNamedCurves.getByName(CURVE_NAME);
        EllipticCurve ellipticCurve = new EllipticCurve(
                new ECFieldFp(x9ECParameters.getCurve().getField().getCharacteristic()),
                x9ECParameters.getCurve().getA().toBigInteger(),
                x9ECParameters.getCurve().getB().toBigInteger()
        );
        ECParameterSpec ecParameterSpec = new ECParameterSpec(
                ellipticCurve,
                new ECPoint(
                        x9ECParameters.getG().getAffineXCoord().toBigInteger(),
                        x9ECParameters.getG().getAffineYCoord().toBigInteger()
                ),
                x9ECParameters.getN(),
                x9ECParameters.getH().intValue()
        );
        BigInteger privateKeyInt = new BigInteger(1, keyBytes);
        ECPrivateKeySpec privSpec = new ECPrivateKeySpec(privateKeyInt, ecParameterSpec);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        return keyFactory.generatePrivate(privSpec);
    }

    /**
     * 将私钥转换为十六进制字符串
     */
    public static String privateKeyToHex(PrivateKey privateKey) {
        if (privateKey instanceof BCECPrivateKey) {
            BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
            return Hex.toHexString(bcecPrivateKey.getD().toByteArray());
        }
        throw new IllegalArgumentException("Invalid private key format");
    }

    /**
     * 将公钥转换为十六进制字符串
     */
    public static String publicKeyToHex(PublicKey publicKey) {
        if (publicKey instanceof BCECPublicKey) {
            BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
            return Hex.toHexString(bcecPublicKey.getQ().getEncoded(false));
        }
        throw new IllegalArgumentException("Invalid public key format");
    }

    /**
     * SM2加密
     */
    public static String encrypt(String plaintext, String hexPublicKey) throws Exception {
        PublicKey publicKey = getPublicKeyFromString(hexPublicKey);
        SM2Engine sm2Engine = new SM2Engine();
        ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(
                ((BCECPublicKey) publicKey).getQ(), getDomainParameters());
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
        byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
        byte[] cipherText = sm2Engine.processBlock(plaintextBytes, 0, plaintextBytes.length);
        return Hex.toHexString(cipherText);
    }

    /**
     * SM2解密
     */
    public static String decrypt(String cipherText) throws Exception {
        PrivateKey privateKey = getPrivateKeyFromString(PRIVATE_HEX_KEY);
        SM2Engine sm2Engine = new SM2Engine();
        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(
                ((BCECPrivateKey) privateKey).getD(), getDomainParameters());
        sm2Engine.init(false, ecPrivateKeyParameters);
        byte[] cipherTextBytes = Hex.decode(cipherText);
        byte[] decryptedText = sm2Engine.processBlock(cipherTextBytes, 0, cipherTextBytes.length);
        return new String(decryptedText);
    }

    public static String decrypt2(String cipherText, String privateHexKey) throws Exception {
        PrivateKey privateKey = getPrivateKeyFromString(privateHexKey);
        SM2Engine sm2Engine = new SM2Engine();
        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(
                ((BCECPrivateKey) privateKey).getD(), getDomainParameters());
        sm2Engine.init(false, ecPrivateKeyParameters);
        byte[] cipherTextBytes = Hex.decode(cipherText);
        byte[] decryptedText = sm2Engine.processBlock(cipherTextBytes, 0, cipherTextBytes.length);
        return new String(decryptedText);
    }

    /**
     * 获取SM2域参数
     */
    private static ECDomainParameters getDomainParameters() {
        X9ECParameters x9ECParameters = GMNamedCurves.getByName(CURVE_NAME);
        return new ECDomainParameters(
                x9ECParameters.getCurve(),
                x9ECParameters.getG(),
                x9ECParameters.getN(),
                x9ECParameters.getH());
    }

    public static void main(String[] args) throws Exception {
        // 1. 生成密钥对
        KeyPair keyPair = SM2Util.generateKeyPair();

        // 2. 获取公钥（十六进制字符串）
        String hexPublicKey = SM2Util.publicKeyToHex(keyPair.getPublic());
        String hexPrivateKey = SM2Util.privateKeyToHex(keyPair.getPrivate());
        System.out.println("公钥: " + hexPublicKey);

        // 3. 使用公钥加密
        String plaintext = "Hello, SM2!";
        String ciphertext = SM2Util.encrypt(plaintext, hexPublicKey);
        System.out.println("密文: " + ciphertext);

//        SM4Util.encrypt()

        // 4. 使用私钥解密（注意：代码中的 PRIVATE_HEX_KEY 需与公钥匹配）
        String decrypted = SM2Util.decrypt2(ciphertext,hexPrivateKey);
        System.out.println("解密结果: " + decrypted);
    }

}
